package com.liming.gateway.routes;

import cn.hutool.json.JSONUtil;
import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;

@Slf4j
@Component
@RequiredArgsConstructor
public class DynamicRoutesLoader {

    //Nacos配置管理,用来获取nacos配置服务,获取配置和添加监听器
    private final NacosConfigManager nacosConfigManager;
    //网关路由重写
    private final RouteDefinitionWriter routeDefinitionWriter;
    private final String dataId = "gateway-routes.json";
    private final String group = "DEFAULT_GROUP";
    private final Set<String> routeIds = new HashSet<>();

    /**
     * 在DynamicRoutesLoader初始化之后执行下面的方法
     * @throws NacosException
     */
    @PostConstruct
    public void initRouteConfigListener() throws NacosException {
        // 1. 项目启动时,先拉取配置,并且添加监听器,监听nacos中的网关路由信息
        String config = nacosConfigManager.getConfigService().getConfigAndSignListener(dataId, group, 5000, new Listener() {
            @Override
            public Executor getExecutor() {
                return null;
            }
            @Override
            public void receiveConfigInfo(String config) {
                // 2. 监听到配置变更, 更新路由表
                updateRouteConfig(config);
            }
        });
        // 3. 第一次读取到配置,也需要更新到路由表
        updateRouteConfig(config);
    }

    public void updateRouteConfig(String config){
        log.debug("监听到路由配置: {}",config);
        // 1. 获取配置转为JAVA对象
        List<RouteDefinition> list = JSONUtil.toList(config, RouteDefinition.class);
        // 2. 删除原来的路由信息表
        for (String routeId : routeIds) {
            Mono<String> mono = Mono.just(routeId);
            routeDefinitionWriter.delete(mono).subscribe();
        }
        // 3. 清空路由ID
        routeIds.clear();
        // 4. 更新路由表
        for (RouteDefinition routeDefinition : list) {
            // 4.1 更新路由表
            Mono<RouteDefinition> mono = Mono.just(routeDefinition);
            routeDefinitionWriter.save(mono).subscribe();
            // 4.2 记录路由id,便于下一次更新时 删除老数据
            routeIds.add(routeDefinition.getId());
        }
    }
}